package a12;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;


/**
 * 模拟JDK动态代理生成的代理类的样子
 * 通过arthas反编译可以看到，JDK代理生成的代理类其实也就是下面这样的
 */
public class $Proxy0 extends Proxy implements A12.Animal{

    static Method eat;
    static Method fly;

    static {
        try {
            eat = A12.Animal.class.getMethod("eat");
            fly = A12.Animal.class.getMethod("fly");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    /**
     * 用户去提供一个实现类传入，在保留框架结构性的同时给予用户自由度
     * 这种写法有点设计模式的感觉呢，感觉好好把设计模式熟悉，多敲敲，对源码的感觉会更好
     */
    public $Proxy0(InvocationHandler handler) {
        super(handler);
    }

    @Override
    public void eat() {
        try {
            Object result = this.h.invoke(this, eat, new Object[0]);
            //从框架的设计角度来讲，这些异常为了让用户感知到，不应该自己处理，而应该抛给用户，由用户自己去处理
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            /**
             * 异常小细节：
             * 将非RuntimeException转换为RuntimeException.因为如果直接throw非RuntimeException
             * 方法上也需要添加throws该异常，但是我们代理的用户方法并不一定有throws该异常，所以转化为
             * RuntimeException(查看UndeclaredThrowableException的注释也可以发现这个异常时给代理
             * 方法使用的)
             */
            throw new UndeclaredThrowableException(e);

        }
    }

    @Override
    public int fly() {
        try {
            Object result = this.h.invoke(this, fly, new Object[0]);
            return (int) result;
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
